#pragma once

unsigned char arr_peer_id[20] = {0x2d,0x55,0x54,0x32,0x32,0x31,0x30,0x2d,0x73,0x62,0xf5,0x4d,0x8a,0xc5,0x5c,0x50,0x94,0xbf,0x39,0x77};
torrent::Client me( std::string(arr_peer_id,arr_peer_id+20));		
bool testPeerClient(torrent::Peer_t peer, std::string& info_hash);

void testSyncClient()
{
	using namespace std;
	using boost::asio::ip::tcp;
	using namespace torrent;
	try
	{
		//std::string host = "announce.torrentsmd.com";
		//std::string service = "6969";
		//std::string reqPath = "/announce?info_hash=HA%e4%7f%a1%b6%40%ef%c1t%9e%f2%ee%84%11%da%baT%90I"
		//	"&peer_id=-UT2210-%2ab%f2%8f%7d%de%89%93w%ad%2b%97&port=64518&uploaded=0"
		//	"&downloaded=0&left=1971905296&corrupt=0&key=4AB3048F&event=started"
		//	"&numwant=200&compact=1&no_peer_id=1"
		//	"&ipv6=2001%3a0%3a4137%3a9e76%3ac4d%3a793%3a883b%3aca36";

		torrent::Bdecoder bd;
		torrent::MetaInfo info = bd.read("test.torrent");
		//torrent::MetaInfo::TrackerList trackers = info.getTrackers();
		using torrent::MetaInfo;

		boost::asio::io_service io_service;
		tcp::socket socket(io_service);

		//for (torrent::MetaInfo::TrackerList::const_iterator i = trackers.begin(); i != trackers.end(); ++i)
		for (string announceStr = info.getNextTracker();;announceStr=info.getNextTracker())
		{
			if ( announceStr.empty() )
				break;

			std::cout << std::endl;

			std::string host;
			std::string service;
			std::string reqPath;

			host = MetaInfo::getHost(announceStr);
			service = MetaInfo::getPort(announceStr);			
			reqPath = MetaInfo::getReqPath(announceStr) + "?" + TrackerRequest::param(me,info._infoHash);

			// Get a list of endpoints corresponding to the server name.
			tcp::resolver resolver(io_service);
			tcp::resolver::query query(host, service);
			tcp::resolver::iterator endpointIterator = resolver.resolve(query);
			boost::system::error_code ec;

			// Try each endpoint until we successfully establish a connection.
			boost::asio::connect(socket, endpointIterator, ec);

			if (!ec)
				std::cout << "connect success, " << host << ":" << service << std::endl;
			else
			{
				std::cout << "connect fail, " << host << ":" << service << ", " << ec.message() << std::endl;
				continue;
			}

			// Form the request. We specify the "Connection: close" header so that the
			// server will close the socket after transmitting the response. This will
			// allow us to treat all data up until the EOF as the content.
			boost::asio::streambuf request;
			std::ostream request_stream(&request);
			request_stream << "GET " << reqPath << " HTTP/1.1\r\n";
			request_stream << "Host: " << host << "\r\n";
			request_stream << "Accept: */*\r\n";
			request_stream << "Connection: close\r\n\r\n";

			// Send the request.
			boost::asio::write(socket, request, ec);
			if (ec)
			{
				std::cout << "write fail, " << host << ":" << service << ", " << ec.message() << std::endl;
				continue;
			}

			// Read the response status line. The response streambuf will automatically
			// grow to accommodate the entire line. The growth may be limited by passing
			// a maximum size to the streambuf constructor.
			boost::asio::streambuf response;
			boost::asio::read_until(socket, response, "\r\n", ec);
			if (ec)
			{
				std::cout << "read_until(header1) fail, " << host << ":" << service << ", " << ec.message() << std::endl;
				continue;
			}

			// Check that response is OK.
			std::istream response_stream(&response);
			std::string http_version;
			response_stream >> http_version;
			std::cout << http_version << std::endl;
			unsigned int status_code;
			response_stream >> status_code;
			std::cout << status_code << std::endl;
			std::string status_message;
			std::getline(response_stream, status_message);
			std::cout << status_message << std::endl;
			if (!response_stream || http_version.substr(0, 5) != "HTTP/")
			{
				std::cout << "Invalid response, " << host << ":" << service << std::endl;
				continue;
			}
			if (status_code != 200)
			{
				std::cout << "Response returned with status code " << status_code << ", " << host << ":" << service << std::endl;
				continue;
			}

			// Read the response headers, which are terminated by a blank line.
			boost::asio::read_until(socket, response, "\r\n\r\n", ec);
			if (ec)
			{
				std::cout << "read_until(header2) fail, " << host << ":" << service << ", " << ec.message() << std::endl;
				continue;
			}

			// Process the response headers.
			std::string header;
			while (std::getline(response_stream, header) && header != "\r")
				std::cout << header << std::endl;
			std::cout << std::endl;

			std::ostringstream oss;

			// Write whatever content we already have to output.
			if (response.size() > 0)
				oss << &response;

			// Read until EOF, writing data to output as we go.
			while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), ec))
				oss << &response;
			if (ec != boost::asio::error::eof)
			{
				std::cout << "read_ fail, " << host << ":" << service << ", " << ec.message() << std::endl;
				continue;
			}
			
			std::string content = oss.str();

			be_node* node = be_decoden(content.c_str(), content.length());
			//be_dump(node);

			std::string s = bencode_helper::get_str(node, "failure reason");
			if ( !s.empty() )
			{
				cout << "fail : " << s << endl;
				continue;
			}

			// reach tracker
			info.trackerSuccess();
			
			be_node* peerNode = bencode_helper::get_node_from_key(node, "peers");
			long long len = be_str_len(peerNode);
			const char* p = peerNode->val.s;

			std::vector<torrent::Peer_t> peers;

			for(size_t i =0;i<static_cast<size_t>(len);)
			{
				unsigned int ip = 0;
				unsigned short port = 0;
				memcpy(&ip,p+i,4);i+=4;
				memcpy(&port,p+i,2);i+=2;

				peers.push_back(std::make_pair(ntohl(ip), ntohs(port)));
				boost::asio::ip::address_v4 addr(peers.back().first);

				if (i / 6 < 6)
					std::cout << "peer end point = "<< addr.to_string() << ":" << peers.back().second << std::endl;
			}
			if (peers.size() > 5)
				std::cout << "... more ...\n" << std::endl;

			for (std::vector<torrent::Peer_t>::const_iterator i = peers.begin(); i != peers.end(); ++i)
			{
				if (testPeerClient(*i, info._infoHash))
					break;
			}

			break;
		}
	}
	catch (std::exception& e)
	{
		std::cout << "Exception: " << e.what() << std::endl;
	}
}

bool testPeerClient(torrent::Peer_t peer, std::string& info_hash)
{
	using boost::asio::ip::tcp;
	std::cout << std::endl;
	try
	{		
		boost::asio::io_service io_service;
		tcp::socket socket(io_service);

		boost::asio::ip::address_v4 addr(peer.first);
		std::string host = addr.to_string();
		std::stringstream ss_peer_port;
		ss_peer_port << peer.second;
		std::string service = ss_peer_port.str();
		std::string reqPath;

		// Get a list of endpoints corresponding to the server name.
		tcp::resolver resolver(io_service);
		tcp::resolver::query query(host, service);
		tcp::resolver::iterator endpointIterator = resolver.resolve(query);
		boost::system::error_code ec;

		// Try each endpoint until we successfully establish a connection.
		boost::asio::connect(socket, endpointIterator, ec);

		if (!ec)
			std::cout << "peer connect success, " << host << ":" << service << std::endl;
		else
		{
			std::cout << "peer connect fail, " << host << ":" << service << ", " << ec.message() << std::endl;
			return false;
		}

		peer_wire::HandShake handshake;
		handshake._infoHash = info_hash;
		handshake._peerID = me._peerID;
		handshake.write(socket, ec);

		if (ec)
		{
			std::cout << "peer handshake_request write fail, " << host << ":" << service << ", " << ec.message() << std::endl;
			return false;
		}
		else
			std::cout << "send handshake success, " << host << ":" << service << std::endl;

		peer_wire::HandShake receivedHandshake;
		receivedHandshake.read(socket, ec);

		if (ec)
		{
			std::cout << "peer handshake_response read fail, " << host << ":" << service << ", " << ec.message() << std::endl;
			return false;
		}
		else
			std::cout << "receive handshake success, " << host << ":" << service << std::endl;

		receivedHandshake.dump();

		peer_wire::Interested interested;
		interested.write(socket, ec);

		if (ec)
		{
			std::cout << "Interested write fail, " << host << ":" << service << ", " << ec.message() << std::endl;
			return false;
		}
		else
			std::cout << "send Interested success, " << host << ":" << service << std::endl;

		peer_wire::MessagePtr msg;

		do
		{
			msg = peer_wire::create_msg_from_socket(socket, ec);
			if (msg)
			{
				msg->dump();
			}
		}
		while (!ec);

		if (ec)
		{
			std::cout << "msg read fail, " << host << ":" << service << ", " << ec.message() << std::endl;
			return false;
		}
		else if (!msg)
		{
			std::cout << "unknown msg, " << host << ":" << service << std::endl;
			return false;
		}

		return true;
	}
	catch (std::exception& e)
	{
		std::cout << "Exception: " << e.what() << std::endl;
		return false;
	}
}